/*
 * Project: Digital Wristwatch
 * Author: Zak Kemble, contact@zakkemble.co.uk
 * Copyright: (C) 2013 by Zak Kemble
 * License: GNU GPL v3 (see License.txt)
 * Web: http://blog.zakkemble.co.uk/diy-digital-wristwatch/
 */

// I2C driver
// Polling based, interrupts aren't really needed for this project since only a few bytes are transfered every-so-often

#include <avr/io.h>
#include <avr/power.h>
#include <util/delay.h>
#include <util/twi.h>
#include "common.h"
#include "drivers/i2c.h"
#include "millis/millis.h"

#define CPU_SPEED		F_CPU
#define BAUDRATE		50000
#define TWI_BAUD(F_SYS, F_TWI)	((F_SYS / (2 * F_TWI)) - 5)
#define TWI_BAUDSETTING			TWI_BAUD(CPU_SPEED, BAUDRATE)


void i2c_init()
{
	TWIC.MASTER.BAUD = TWI_BAUDSETTING;
	TWIC.MASTER.CTRLA = TWI_MASTER_ENABLE_bm;
	//TWIC.MASTER.CTRLB = TWI_MASTER_SMEN_bm;
	TWIC.MASTER.STATUS = TWI_MASTER_BUSSTATE_IDLE_gc;
}

bool i2c_start(uint8_t addr)
{
	TWIC.MASTER.ADDR = addr;
	while(!(TWIC.MASTER.STATUS & (TWI_MASTER_WIF_bm | TWI_MASTER_RIF_bm)));

	if(TWIC.MASTER.STATUS & TWI_MASTER_RXACK_bm) {
		return false;
	}
	return true;
}

void i2c_stop()
{
	TWIC.MASTER.CTRLC = TWIC.MASTER.CTRLC | TWI_MASTER_CMD_STOP_gc;//Stop
	//If not check STOP condition before enter power down mode, TWI will pull scl & sda low when
	//CPU enter power down mode, this will waste a lot of system power
	while((TWIC.MASTER.STATUS & TWI_MASTER_BUSSTATE_gm) != 0x01);//wait for stop condition
}

bool i2c_write(byte _data)
{
	TWIC.MASTER.DATA = _data;
	while(!(TWIC.MASTER.STATUS & TWI_MASTER_WIF_bm));

	if(TWIC.MASTER.STATUS & TWI_MASTER_RXACK_bm) {
		return false;
	}
	return true;
}

bool i2c_read(byte* data, bool ack)
{
	millis_t start;

	start = millis();
	while(!(TWIC.MASTER.STATUS & TWI_MASTER_RIF_bm)) {
		if((millis() - start) > 1) {//1ms timeout
			return false;
		}
	}
	*data = TWIC.MASTER.DATA;
	
	if(ack) {
		TWIC.MASTER.CTRLC = TWI_MASTER_CMD_RECVTRANS_gc;
	} else {
		TWIC.MASTER.CTRLC = TWI_MASTER_ACKACT_bm;
	}
	
	return true;
}

// I2C dev read
bool i2c_dev_read(byte dev_addr, byte reg_addr, byte* data, byte count)
{
	// Start
	if(!i2c_start(dev_addr)) {
		i2c_stop();
		return false;
	}

	if(!i2c_write(reg_addr)) { // Register address
		i2c_stop();
		return false;
	}

	// Repeat start
	if(!i2c_start(dev_addr | 0x01)) {
		i2c_stop();
		return false;
	}

	// Read data
	for(byte i=0;i<count;i++)
	{
		if(!i2c_read(&data[i], (i < (count - 1)) ? I2C_ACK : I2C_NACK)) {
			i2c_stop();
			return false;
		}
	}

	// Stop
	i2c_stop();

	return true;
}

// I2C dev write
bool i2c_dev_write(byte dev_addr, byte reg_addr, byte data)
{
	// Star/
	if(!i2c_start(dev_addr)) {
		i2c_stop();
		return false;
	}

	if(!i2c_write(reg_addr)) { // Register address
		i2c_stop();
		return false;
	}

	if(!i2c_write(data)) { 
		i2c_stop();
		return false;
	}

	// Stop
	i2c_stop();

	return true;
}

